#pragma once

#include <Arduino.h>

class PWM_out
{
private:
    typedef PWM_out self;
public:
    PWM_out(int pin, int channel, int resolution, int freq)
        : _pin(pin)
        , _channel(channel)
        , _resolution(resolution)
        , _freq(freq)
    {}

    void init()
    {
        ledcSetup(_channel, _freq, _resolution);
        ledcAttachPin(_pin, _channel);
    }
    void out(int duty)
    {
        ledcWrite(_channel, duty);
    }
    void destroy()
    {
        ledcDetachPin(_pin);
    }

    ~PWM_out()
    {
        destroy();
    }
protected:
    int _pin;
    int _channel;
    int _resolution;
    int _freq;
};

class Servo : public PWM_out
{
private:
    typedef Servo self;
public:
    Servo(int pin, int channel, int resolution, int degree_max = 180, int degree_min = 0)
        : PWM_out(pin, channel, resolution, 50)
        , _degree_max(degree_max)
        , _degree_min(degree_min)
    {
        if (_degree_min < 0)
            _degree_min = 0;
        if (_degree_max > 180)
            _degree_max = 180;
    }

    void degree_control(int degree)
    {
        //20ms 周期内，高电平持续时长 0.5-2.5 ms，对应 0-180 度舵机角度
        float min_width = 0.5 / (1000 / _freq) * pow(2, _resolution);
        float max_width = 2.5 / (1000 / _freq) * pow(2, _resolution);
        if (degree <= _degree_min)
            degree = _degree_min;
        if (degree >= _degree_max)
            degree = _degree_max;
        out((int)(((max_width - min_width) / 180) * degree + min_width));
    }
private:
    int _degree_max;
    int _degree_min;
};

class motor : public PWM_out
{
private:
    typedef motor self;
public:
    motor(int pin, int channel, int resolution, int freq, int dir_a_pin, int dir_b_pin)
        : PWM_out(pin, channel, resolution, 50)
        , _dir_a_pin(dir_a_pin)
        , _dir_b_pin(dir_b_pin)
    {}
    void init()
    {
        PWM_out::init();
        pinMode(_dir_a_pin, OUTPUT);
        pinMode(_dir_b_pin, OUTPUT);
        out(0);
    }
    void out(int duty)
    {
        if (duty >= 0)
        {
            digitalWrite(_dir_a_pin, true);
            digitalWrite(_dir_b_pin, false);
        }
        else
        {
            digitalWrite(_dir_a_pin, false);
            digitalWrite(_dir_b_pin, true);
            duty = -duty;
        }
        PWM_out::out(duty);
    }
private:
    int _dir_a_pin;
    int _dir_b_pin;
};
